import { computed, Ref } from 'vue'
import { useDrag, MoveData } from '@/utils'
import {
  DragBox,
  DragState,
  ResizeData,
  ResizeRange,
  ResizeHandle,
  MiddleHandles,
  TopHandles,
  BottomHandles,
  LeftHandles,
  RightHandles
} from './type'

/** 初始状态 */
interface StartState {
  /** 初始左边 x 坐标 */
  x0: number

  /** 初始右边 x 坐标 */
  x1: number

  /** 初始中点 x 坐标 */
  ox: number

  /** 初始上方 y 坐标 */
  y0: number

  /** 初始下方 y 坐标 */
  y1: number

  /** 初始中点 y 坐标 */
  oy: number

  /** 初始宽度 */
  sw: number

  /** 初始高度 */
  sh: number

  /** 父节点宽度 */
  pw: number

  /** 父节点高度 */
  ph: number

  /** 缩放比 */
  ratio: number

  /** 句柄 */
  handle: ResizeHandle
}

/** 调整大小 */
export default function useResize({
  box,
  state,
  aspectRatio,
  onStart,
  onResizing,
  onEnd
}: {
  /** 盒子数据 */
  box: DragBox
  /** 状态 */
  state: DragState
  /** 缩放 */
  aspectRatio: Ref<number>
  /** 大小调整开始 */
  onStart?: (e: MouseEvent | TouchEvent) => void | false
  /** 大小调整中 */
  onResizing?: (e: MouseEvent | TouchEvent, resize: ResizeData) => void
  /** 大小调整结束 */
  onEnd?: (e: MouseEvent | TouchEvent, data: DragState) => void
}) {
  /** 缩放级别 */
  const zoom = computed(() => box.zoom)

  // 调整大小范围
  let range: ResizeRange | undefined

  /** 获取调整大小范围 */
  function getRange(): ResizeRange {
    const { pw, ph, resizeParent } = state
    let { minW, maxW, minH, maxH } = box
    let [maxX, maxY] = [Infinity, Infinity]

    // 比例
    const ratio = aspectRatio.value

    // 最小坐标
    const min = resizeParent ? 0 : -Infinity

    if (ratio) {
      minW = Math.max(minW, minH * ratio, 0)
      minH = Math.max(minH, minW / ratio, 0)
    }

    if (resizeParent) {
      maxW = Math.min(maxW, pw)
      maxH = Math.min(maxH, ph)

      if (ratio) {
        maxW = Math.min(maxW, maxH * ratio)
        maxH = Math.min(maxH, maxW / ratio)
      }

      maxX = pw - minW
      maxY = ph - minH
    }

    return {
      minX: min,
      maxX,
      minY: min,
      maxY,
      minW,
      maxW,
      minH,
      maxH
    }
  }

  /** 拖动处理 */
  const onMoving = (e: MouseEvent | TouchEvent, { x: moveX, y: moveY }: MoveData, startState: StartState) => {
    if (!range) return

    const { resizeAtCenter } = state
    const { handle, x0, y0, x1, y1, ox, oy, sw, sh, pw, ph, ratio } = startState
    let { x0: x, y0: y, sw: w, sh: h } = startState
    let { minX, minY, maxX, maxY, minW, minH, maxW, maxH } = range

    // 确定盒子尺寸范围 - 中心点
    if (resizeAtCenter) {
      maxW = Math.min(maxW, ox * 2, (pw - ox) * 2)
      maxH = Math.min(maxH, oy * 2, (ph - oy) * 2)
    }

    // 确定盒子尺寸范围 - 等比调整大小
    if (ratio) {
      // 不支持中间的句柄
      if (MiddleHandles.includes(handle)) {
        return
      }

      if (!resizeAtCenter) {
        if (TopHandles.includes(handle)) {
          maxH = Math.min(maxH, y1)
        } else if (BottomHandles.includes(handle)) {
          // 最大高度
          maxH = Math.min(maxH, ph - y0)
        }
      }

      // 等比最大宽度
      maxW = Math.min(maxW, ratio * maxH)
    }

    // 中心点调整大小
    if (resizeAtCenter) {
      maxX = Math.min(maxX, ox - minW / 2)
      minX = Math.max(minX, ox - maxW / 2, pw - (pw - ox) * 2)
      maxY = Math.min(maxY, oy - minH / 2)
      minY = Math.max(minY, oy - maxH / 2, ph - (ph - oy) * 2)
    }

    // 锁定 x1，更改 X 坐标和宽度
    if (LeftHandles.includes(handle)) {
      maxX = Math.min(maxX, x1 - minW)
      minX = Math.max(minX, x1 - maxW)

      x = x0 + moveX

      if (x < minX) x = minX
      else if (x > maxX) x = maxX

      w = resizeAtCenter ? (ox - x) * 2 : x1 - x
    }

    // 锁定 x0，更改宽度
    if (RightHandles.includes(handle)) {
      if (!resizeAtCenter) {
        maxW = Math.min(maxW, pw - x0)
      }

      if (resizeAtCenter) {
        x = x0 - moveX

        if (x < minX) x = minX
        else if (x > maxX) x = maxX
      }

      w = resizeAtCenter ? sw + (x0 - x) * 2 : sw + moveX

      if (w > maxW) w = maxW
    }

    // 锁定 y1，更改 Y 坐标和高度
    if (TopHandles.includes(handle)) {
      maxY = Math.min(maxY, y1 - minH)
      minY = Math.max(minY, y1 - maxH)

      if (ratio) {
        if (resizeAtCenter) {
          y = y1 - (x1 - x) / ratio
        } else {
          y = y1 - w / ratio
        }
      } else {
        y = y0 + moveY
      }

      if (y < minY) y = minY
      else if (y > maxY) y = maxY

      h = resizeAtCenter ? (oy - y) * 2 : y1 - y
    }

    // 锁定 y0，更改高度
    if (BottomHandles.includes(handle)) {
      if (!resizeAtCenter) {
        maxH = Math.min(maxH, ph - y0)
      }

      if (resizeAtCenter) {
        if (ratio) {
          /**
           * 等比以宽度为基准确定高度
           * y = oy - h / 2
           *   = oy - w / ratio / 2
           */
          y = oy - w / ratio / 2
        } else {
          y = y0 - moveY

          if (y < minY) y = minY
          else if (y > maxY) y = maxY
        }
      }

      h = resizeAtCenter ? sh + (y0 - y) * 2 : sh + moveY

      if (h > maxH) h = maxH
    }

    // 等比以宽度为基准确定高度
    if (ratio) {
      h = w / ratio
    }

    if (w < minW) w = minW
    if (h < minH) h = minH

    onResizing?.(e, { x, y, w, h })
  }

  return useDrag<StartState, ResizeHandle>({
    zoom,

    onStart(e, start, handle) {
      state.active = true
      state.update()
      range = getRange()

      if (onStart?.(e) === false) return false

      const { x, y, w, h, pw, ph } = state

      return {
        x0: x,
        x1: x + w,
        ox: x + w / 2,
        y0: y,
        y1: y + h,
        oy: y + h / 2,
        sw: w,
        sh: h,
        pw,
        ph,
        ratio: aspectRatio.value,
        handle: handle as ResizeHandle
      }
    },

    onMoving,

    onEnd(e, moved) {
      moved && onEnd?.(e, state)
    }
  })
}
